//Includes

#include "cp/cplo.cpp"
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <string>
#include <list>
#include <cmath>
#include <fstream>

using namespace std;


//Constantes

const int SCREEN_WIDTH = 628;
const int SCREEN_HEIGHT = 394;
const int SCREEN_BPP = 32;
const double REPULSAO = 40000;
const double ATRACAO = 20;
const int x00 = 50;
const int y00 = 192;
const int distmax = 1000;
const int fps = 60;
const double VMAX = 2;
const int distancia = 20;
const int x02 = SCREEN_WIDTH-60;
const int y02 = SCREEN_HEIGHT/2;
const int ball_x0 = SCREEN_WIDTH/2;
const int ball_y0 = SCREEN_HEIGHT/2;
const double cte_tecla = VMAX*2;
const double REPULSAO_COMP = 10000;
const int TEMPO = 3;
const int placar_y = 10;


//Superficies

SDL_Surface *image = NULL;
SDL_Surface *screen = NULL;
SDL_Surface *black = NULL;
SDL_Surface *comp = NULL;
SDL_Surface *player = NULL;
SDL_Surface *ball = NULL;
SDL_Surface *ini = NULL;
SDL_Surface *button = NULL;
SDL_Surface *pausar = NULL;
SDL_Surface *n0 = NULL;
SDL_Surface *n1 = NULL;
SDL_Surface *n2 = NULL;
SDL_Surface *n3 = NULL;
SDL_Surface *n4 = NULL;
SDL_Surface *n5 = NULL;
SDL_Surface *n6 = NULL;
SDL_Surface *n7 = NULL;
SDL_Surface *n8 = NULL;
SDL_Surface *n9 = NULL;
SDL_Surface *max_score = NULL;
SDL_Surface *score = NULL;

//Eventos

SDL_Event event;


//Funcoes

double distanciaf (int x1, int y1, int px, int py) {
	if (x1 != px && y1 != py)
		return sqrt(pow(static_cast<double>(x1-px),2)+pow(static_cast<double>(y1-py),2));
	else
		return fabs(x1-px+y1-py);
	}



SDL_Surface *load_image( std::string filename )
{
    SDL_Surface* loadedImage = NULL;
    SDL_Surface* optimizedImage = NULL;

    loadedImage = IMG_Load( filename.c_str() );

    if( loadedImage != NULL )
    {
        optimizedImage = SDL_DisplayFormat(loadedImage );
        SDL_FreeSurface( loadedImage );
    }
    return optimizedImage;

}

SDL_Surface *load_image2( std::string filename )
{
    SDL_Surface* loadedImage = NULL;
    SDL_Surface* optimizedImage = NULL;

    loadedImage = IMG_Load( filename.c_str() );

    if( loadedImage != NULL )
    {
        optimizedImage = SDL_DisplayFormatAlpha(loadedImage );
        SDL_FreeSurface( loadedImage );
    }
    return optimizedImage;

}



void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination )
{
    SDL_Rect offset;
    offset.x = x;
    offset.y = y;
    SDL_BlitSurface( source, NULL, destination, &offset );
}


void escreve_numeros (int y, int n, SDL_Surface *imagem, SDL_Surface** destino) {
	int digitos, bs;
	for (digitos = 0; pow(10.0,digitos) < n; digitos++);
        int tmp = n;
	int as = SCREEN_WIDTH/2 - (digitos/2)*12 - 96;
        apply_surface(as, y, imagem, *destino);
	as += 96;
	if (digitos % 2 == 1)
		as -= 6;
	for (; digitos != 0; as+= 12, digitos--) {
		bs = (int)(tmp/pow(10.0, digitos-1));
		tmp -= static_cast<int>(pow(10.0, digitos-1)*bs);
		switch (bs) {
			case 0:
				apply_surface (as, y, n0, screen);
				break;
			case 1:
				apply_surface (as, y, n1, screen);
				break;
			case 2:
				apply_surface (as, y, n2, screen);
				break;
			case 3:
				apply_surface (as, y, n3, screen);
				break;
			case 4:
				apply_surface (as, y, n4, screen);
				break;
			case 5:
				apply_surface (as, y, n5, screen);
				break;
			case 6:
				apply_surface (as, y, n6, screen);
				break;
			case 7:
				apply_surface (as, y, n7, screen);
				break;
			case 8:
				apply_surface (as, y, n8, screen);
				break;
			case 9:
				apply_surface (as, y, n9, screen);
				break;
			}
		}
	}

void escreve_numeros2 (int y, int n, SDL_Surface *imagem, SDL_Surface** destino) {
	int digitos, bs;
	for (digitos = 0; pow(10.0,digitos) < n; digitos++);
        int tmp = n;
	int as = SCREEN_WIDTH - (digitos/2)*12 - 116;
        apply_surface(as, y, imagem, *destino);
	as += 96;
	if (digitos % 2 == 1)
		as -= 6;
	for (; digitos != 0; as+= 12, digitos--) {
		bs = (int)(tmp/pow(10.0, digitos-1));
		tmp -= static_cast<int>(pow(10.0, digitos-1)*bs);
		switch (bs) {
			case 0:
				apply_surface (as, y, n0, screen);
				break;
			case 1:
				apply_surface (as, y, n1, screen);
				break;
			case 2:
				apply_surface (as, y, n2, screen);
				break;
			case 3:
				apply_surface (as, y, n3, screen);
				break;
			case 4:
				apply_surface (as, y, n4, screen);
				break;
			case 5:
				apply_surface (as, y, n5, screen);
				break;
			case 6:
				apply_surface (as, y, n6, screen);
				break;
			case 7:
				apply_surface (as, y, n7, screen);
				break;
			case 8:
				apply_surface (as, y, n8, screen);
				break;
			case 9:
				apply_surface (as, y, n9, screen);
				break;
			}
		}
	}



bool init()
{
    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
        return false;

    screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );

    if( screen == NULL )
        return false;

    SDL_WM_SetCaption( "WartFoG - The Game", NULL );

    return true;
}



bool load_files()
{
    image = load_image("images/wall.gif");
    black = load_image("images/campo.jpg");
    comp = load_image("images/circle.jpg");
    player = load_image("images/player.jpg");
    ball = load_image2("images/ball.png");
    ini = load_image("images/ini.jpg");
    button = load_image("images/button.jpg");
    pausar = load_image("images/pause.jpg");
    n0 = load_image("images/0.jpg");
    n1 = load_image("images/1.jpg");
    n2 = load_image("images/2.jpg");
    n3 = load_image("images/3.jpg");
    n4 = load_image("images/4.jpg");
    n5 = load_image("images/5.jpg");
    n6 = load_image("images/6.jpg");
    n7 = load_image("images/7.jpg");
    n8 = load_image("images/8.jpg");
    n9 = load_image("images/9.jpg");
    max_score = load_image("images/max_score.jpg");
    score = load_image("images/score.jpg");

    if(image == NULL || black == NULL || comp == NULL || player == NULL || ini == NULL || button == NULL || max_score == NULL)
        return false;

    return true;
}


void clean_up()
{
    SDL_FreeSurface(image);
    SDL_Quit();
}


//Main

int main( int argc, char* args[] )
{

    //Inicializacao

    if(init() == false)
        return 1;

    if(load_files() == false)
        return 1;

    if(SDL_Flip(screen) == -1)
        return 1;

    //Meleca de variaveis

    int n = 0;
    int mx, my;
    bool quit = false;
    cplo tresultante;
    list<int>lx, ly;
    list<int>x, y;
    int tx, ty;
    int px = x02, py = y02;
    list<double> vx, vy;
    double tvx, tvy;
    double pvx = 0, pvy = 0;
    bool start = true;
    int ball_x = ball_x0;
    int ball_y = ball_y0;
    double ball_vx = 0;
    double ball_vy = 0;
    bool up, down, left, right;
    up = down = left = right = false;
    long long int player_points = 0;
    long tempo = 0;
    int add = 0;
    bool comp_retorna;
    int loser = 0;
    long long int max_points = 0;
    bool sair = false;
    int digitos, as, bs;
    double tmp;
    list<int>::iterator it_x, it_y;
    list<double>::iterator it_vx, it_vy;

    //Inicio do nucleo

    apply_surface(0, 0, black, screen);
    apply_surface(0, 0, ini, screen);
    apply_surface((SCREEN_WIDTH/2)-64, (SCREEN_HEIGHT/2)-32, button, screen);
    SDL_Flip(screen);

    while(sair == false)
    	while(SDL_PollEvent(&event))
		if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_SPACE)
			sair = true;
		else if (event.type == SDL_QUIT) {
			quit = true;
			sair = true;
			}


    while( quit == false )
    {
	if ((tempo/60)/TEMPO >= n && start == true) {
		n++;
		x.push_back (x00+add);
		add += 10;
		if (add+x00 > SCREEN_WIDTH-20) add = 0;
		y.push_back (y00);
		vx.push_back (0);
		vy.push_back (0);
		lx.push_back (SCREEN_WIDTH-px);
		ly.push_back (SCREEN_HEIGHT-py);
		}

        while(SDL_PollEvent(&event))
        {
            switch (event.type) {
                case SDL_QUIT:
                    quit = true;
                    break;

                case SDL_MOUSEBUTTONDOWN:
                    lx.push_back (event.motion.x);
                    ly.push_back (event.motion.y);
                    break;

                case SDL_KEYDOWN:
                    if (start == false)
                        switch (event.key.keysym.sym) {
                            case SDLK_SPACE:
                        		start = true;
                                break;

/*                           case SDLK_l:
                                ifstream load ("save.wtf");
                                if (load != NULL) {
                                    int n_tmp, i_tmp, j_tmp;
                                    load >> n >> mx >> my >> n_tmp;
                                    lx.clear();
                                    ly.clear();
                                    for (int i = 0; i < n_tmp; i++) {
                                        load >> i_tmp >> j_tmp;
                                        lx.push_back(i_tmp);
                                        ly.push_back(j_tmp);
                                        }
                                    load >> px >> py >> n_tmp;
                                    vx.clear();
                                    vy.clear();
                                    for (int i = 0; i < n_tmp; i++) {
                                        load >> i_tmp >> j_tmp;
                                        vx.push_back(i_tmp);
                                        vy.push_back(j_tmp);
                                        }
                                    load >> pvx >> pvy >> ball_x >> ball_y;
                                    load >> ball_vx >> ball_vy >> player_points;
                                    load >> tempo >> add >> loser;
                                    load >> max_points;
                                    }
                                load.close();
                                break;

                            case SDLK_s:
                                ofstream save ("save.wtf");
                                if (save != NULL) {
                                save << n << " " << mx << " " << my;
                                save << " " << lx.size();
                                for (it_x = lx.begin(), it_y = ly.begin(); it_x != lx.end(); it_x++, it_y++)
                                    save << " " << *it_x << " " << *it_y;
                                save << " " << px << " " << py << " " << vx.size();
                                for (it_vx = vx.begin(), it_vy = vy.begin(); it_vx != vx.end(); it_vx++, it_vy++)
                                    save << " " << *it_vx << " " << *it_vy;
                                save << " " << pvx << " " << pvy << " " << ball_x << " ";
                                save << ball_y << " " << ball_vx << " " << ball_vy << " ";
                                save << player_points << " " << tempo;
                                save << " " << add << " " << loser << " " << max_points;
                                save.close();
                                }
                                break;

                                */

                                }
                    else {
			switch (event.key.keysym.sym) {
				case SDLK_SPACE:
                    start = false;
					break;

				case SDLK_UP:
					up = true;
					break;

				case SDLK_DOWN:
					down = true;
					break;

				case SDLK_LEFT:
					left = true;
					break;

				case SDLK_RIGHT:
					right = true;
					break;
				}
			}
                    break;

		case SDL_KEYUP:
		    if (start == true)
			switch (event.key.keysym.sym) {
				case SDLK_UP:
					up = false;
					break;

				case SDLK_DOWN:
					down = false;
					break;

				case SDLK_LEFT:
					left = false;
					break;

				case SDLK_RIGHT:
					right = false;
					break;

				}
            break;
            }
        }

		pvx = static_cast<int>(right)*cte_tecla + static_cast<int>(left)*(-cte_tecla);
		pvy = static_cast<int>(down)*cte_tecla + static_cast<int>(up)*(-cte_tecla);

    apply_surface(0, 0, black, screen);
	apply_surface(px-10, py-10, player, screen);
	apply_surface(ball_x-5, ball_y-5, ball, screen);
        if (start == false)
		apply_surface((SCREEN_WIDTH/2)-96, (SCREEN_HEIGHT/2)-48, pausar, screen);
	for (it_x = lx.begin(), it_y = ly.begin(); it_x != lx.end(); it_x++, it_y++)
		apply_surface((*it_x)-10, (*it_y)-10, image, screen);

	for (it_x = x.begin(), it_y = y.begin(); it_x != x.end(); it_x++, it_y++)
	        apply_surface((*it_x)-10, (*it_y)-10, comp, screen);
        if (start) {

	    for (int i = 0; i < n-loser; i++) {

		comp_retorna = 1;

		tvx = vx.front();
		vx.pop_front();
		tvy = vy.front();
		vy.pop_front();
		tx = x.front();
		x.pop_front();
		ty = y.front();
		y.pop_front();

        	tresultante.reset();
        	tresultante.setCteRepulsiva(REPULSAO);
        	tresultante.setObjeto(tx, ty);
        	tresultante.setCteAtrativa(ATRACAO);
		tresultante.addAtrativa(px, py);

        	for (it_x = lx.begin(), it_y = ly.begin(); it_x != lx.end(); it_x++, it_y++)
            		tresultante.addRepulsiva(*it_x, *it_y);

		tresultante.setCteRepulsiva(REPULSAO_COMP);

		for (it_x = x.begin(), it_y = y.begin(); it_x != x.end(); it_x++, it_y++)
			if (tx != (*it_x) && ty != (*it_y))
				tresultante.addRepulsiva(*it_x, *it_y);
            	tvx += tresultante.getResultanteX(tx, ty);

		if (tvx > VMAX) tvx = VMAX;
	    	else if (tvx < -VMAX) tvx = -VMAX;

            	tvy += tresultante.getResultanteY(tx, ty);

	        if (tvy > VMAX) tvy = VMAX;
            	else if (tvy < -VMAX) tvy = -VMAX;

		tx += static_cast<int> (tvx);
		if (tx % 2 == 1) tx++;
		ty += static_cast<int> (tvy);
		if (ty % 2 == 1) ty++;


		if (ty > SCREEN_HEIGHT) {
			ty = SCREEN_HEIGHT;
			tvy *= -0.7;
           	} else if (ty < 0) {
			ty = 0;
			tvy *= -0.7;
                	}

		if (ty > SCREEN_HEIGHT) {
			ty = SCREEN_HEIGHT;
			tvy *= -0.7;
           	} else if (ty < 0) {
			ty = 0;
			tvy *= -0.7;
                	}

		if (distanciaf(px, py, tx, ty) < distancia) {
			player_points = static_cast<long long int>(player_points * 0.5);
			comp_retorna = 0;
			loser++;
			}

		if (comp_retorna == 1) {
			vx.push_back (tvx);
			vy.push_back (tvy);
			x.push_back(tx);
			y.push_back(ty);
			}

		}

	    for (int i = 0; i < lx.size(); i++) {
		tx = lx.front();
		lx.pop_front();
		ty = ly.front();
		ly.pop_front();
		if (distanciaf(px, py, tx, ty) < distancia)
			player_points = static_cast<long long int>(player_points * 0.5);
		else {
			lx.push_back(tx);
			ly.push_back(ty);
			}
		}

	    if (ball_vx > VMAX*5) ball_vx = VMAX*5;
            else if (ball_vx < -VMAX*5) ball_vx = -VMAX*5;

	    if (ball_vy > VMAX*5) ball_vy = VMAX*5;
            else if (ball_vy < -VMAX*5) ball_vy = -VMAX*5;

	    ball_vx *= 0.99;
	    ball_vy *= 0.99;

	    ball_x += static_cast<int> (ball_vx);
	    if (ball_x % 2 == 1)
	       ball_x++;
	    ball_y += static_cast<int> (ball_vy);
	    if (ball_y % 2 == 1)
	       ball_y++;

	    if (ball_x > SCREEN_WIDTH) {
		ball_x = SCREEN_WIDTH;
		ball_vx *= -0.7;
	    } else if (ball_x < 0) {
		ball_x = 0;
		ball_vx *= -0.7;
		}

	    if (ball_y > SCREEN_HEIGHT) {
		ball_y = SCREEN_HEIGHT;
		ball_vy *= -0.7;
	    } else if (ball_y < 0) {
		ball_y = 0;
		ball_vy *= -0.7;
                }


	    if (px > SCREEN_WIDTH) {
		px = SCREEN_WIDTH;
		pvx *= -0.7;
	    } else if (px < 0) {
		px = 0;
		pvx *= -0.7;
		}

	    if (py > SCREEN_HEIGHT) {
		py = SCREEN_HEIGHT;
		pvy *= -0.7;
	    } else if (py < 0) {
		py = 0;
		pvy *= -0.7;
                }

            px += static_cast<int> (pvx);
            if (px % 2 == 1)
               px++;
            py += static_cast<int> (pvy);
            if (py % 2 == 1)
               py++;

            }

	escreve_numeros (placar_y, player_points, score, &screen);
	escreve_numeros (SCREEN_HEIGHT-25, max_points, max_score, &screen);


        if (SDL_Flip(screen) == -1)
            return 1;

	if (distanciaf(ball_x, ball_y, px, py) < distancia) {
		ball_vx += pvx*1.5;
		pvx *= 0.5;
		ball_vy += pvy*1.5;
		pvy *= 0.5;
		player_points *= 2;
		ball_x = SCREEN_WIDTH-px;
		if (ball_x % 2 == 1)
		   ball_x++;
		ball_y = SCREEN_HEIGHT-py;
		if (ball_y % 2 == 1)
		   ball_y++;
		}

        SDL_Delay (1000/fps);

	if (start == true) {
		player_points++;
		if (player_points > max_points) max_points = player_points;
		tempo++;
		}
    }
    clean_up();
    return 0;
}
